# -*- coding: utf-8 -*-
"""
Created on Mon May 18 13:51:53 2026
@author: S. Tomb

Description: "This code is made to test a theory about the game of Blackjack.
Specifically the theory that one should statistically hit if they are at 16 or below. 
This code simulates a string of blackjack games where the player (simulated) will 
hit if they are below 16 or stay if they are above it. The simulation will begin by asking the user:
How much money does the player start with?
How much money does the player bet each round?
How many rounds does the player play (if they don't bankrupt)?
How many decks of cards are used by the dealer?"

Variables:
-Starting Money- Represents the money the AI player starts with
-Bet- Represents the ammount of money subtracted from the AI player's total cash at the beginning of each round
-rounds- How many rounds of Blackjack are played if player does not bankrupt
-decks- How many decks are used by the dealer
-Winnings- Stores how much money has been won by the player AI
-Losses- Stores how much money has been lost by the player AI
-Player-hand- stores the current score of the player AI's hand
-Dealer-hand- stores the current score of the dealer AI's hand
-Player-cards- Stores the cards in the Player AI's hand
-Dealer-cards- Stores the cards in the Dealer AI's hand
-Money- How much money the Player AI has currently
-Bankruptcy- Stores whether or not the player has gone bankrupt, ends game if true
-Suits- Stores the four suits of cards, used to properly generate all of the cards
-Ranks- All of the numbered/faces cards can have, used to properly generate all of the cards
-choice- Stores user input to change values from defaults
-Hand- Used by multiple functions to represent the cards in the Player/Dealer's hand
-Score- Used by multiple functions to represent the Player/Dealer's score
"""
#importing necessary functions
from itertools import product 
from random import shuffle

#Initialize Variables
Starting_Money = 10000
Bet = 5
rounds = 100
decks = 1
user_input = 0 
Winnings = 0
Losses = 0 
Player_hand = 0
Dealer_hand = 0
Player_cards = []
Dealer_cards = []
Money = Starting_Money
Bankruptcy = False

#Initializing variables for the deck
Suits = ["H", "D", "C", "S"] 
Ranks = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'] 

#Introduction
print("Welcome to the Blackjack Probability machine, this machine tests the hypothesis that the optimal strategy to winning Blackjack is hitting if you at 16 or below.")
print("Cards are depicted as a number and than the first letter of the suit. H is equal to hearts, D is equal to diamonds, C is equal to clubs, and S is equal to spades.")
print("")
print("Disclaimer: This is a purely statistical test, it is not meant to be advice for real world gambling as it only accounts for known variables. As casinos are known to cheat, it is not possible to have a 100% accurate simulation and I do not condone gambling.")
print("")

#Gets user defined variables to make this test modular
def getUserInput():
    global Starting_Money, Bet, rounds, decks, Money
    
    print("Default values are Starting_Money: " + str(Starting_Money) + " Bet: " + str(Bet) + " rounds: " + str(rounds) + " decks: " + str(decks)) 
    choice = int(input("Press 1 for default values, 2 for custom values: "))
    print("")
    while choice not in [1, 2]:
        choice = int(input("Invalid input. Press 1 or 2: "))
        print("")
    if choice == 1:
        return

    Starting_Money = int(input("Starting money: "))
    print("")

    while Starting_Money <= 0:
        Starting_Money = int(input("Enter a positive number: "))

    Bet = int(input("Bet amount: "))
    print("")
    while Bet <= 0 or Bet >= Starting_Money:
        Bet = int(input("Bet must be positive and less than starting money: "))
        print("")

    rounds = int(input("Number of rounds: "))
    print("")
    while rounds <= 0:
        rounds = int(input("Enter a positive number: "))
        print("")

    decks = int(input("Number of decks: "))
    print("")
    while decks <= 0:
        decks = int(input("Enter a positive number: "))
        print("")

    Money = Starting_Money

#Creates a deck of cards based on how many decks were specified by the user 
def Create_Deck():
    global decks
    deck = []

    for _ in range(decks):
        for rank, suit in product(Ranks, Suits):
            deck.append(rank + suit)

    shuffle(deck)
    return deck

# When the player AI wins a round
def winRound():
    global Bet, Winnings, Money
    print("Player has won round")
    print("")
    Winnings += Bet
    Money += Bet * 2
    print("Player now has " + str(Money))
    print("")

# When the Player and Dealer AI win a round
def tieRound():
    global Bet, Money
    print("Player has tied with Dealer")
    print("")
    Money += Bet
    print("Player now has " + str(Money))
    print("")

# When the Dealer AI wins a round
def lossRound():
    global Bet, Losses, Money
    print("Player has lost round")
    print("")
    Losses += Bet
    print("Player now has " + str(Money))
    print("")

# When round begins, takes bet from the player
def roundBet():
    global Bet, Money
    Money -= Bet
    print("Player now has " + str(Money))
    print("")

#Checks if player has gone bankrupt at end of round
def checkBankruptcy():
    global Money
    if (Money <= 0):
        print("Player has gone bankrupt")
        print("")
        return True
    return False

#compares the scores of the player and dealer
def compareScore():
    global Player_hand, Dealer_hand
    if ((Player_hand <= 21) and (Player_hand > Dealer_hand)):
        winRound()
    elif ((Dealer_hand <= 21) and (Dealer_hand > Player_hand)):
        lossRound()
    elif (Dealer_hand == Player_hand):
        tieRound()
    elif ((Dealer_hand <= 21) and (Player_hand > 21)):
        lossRound()
    elif ((Player_hand <= 21) and (Dealer_hand > 21)):
        winRound()
    else: 
        print("error, unhandled event")
        print("")

#Gives a card to the specified AI
def dealCard(Hand):
    global deck
    Hand.append(deck[0])
    deck.append(deck[0])
    deck.pop(0)
    return Hand

#Shuffles deck if the current round is equal to a multiple of 52 (the number of cards in a standard deck)
def shuffleDeck(rounds):
    global deck, decks
    if rounds % (52 * decks) == 0:
        shuffle(deck)

#Calculates the score of a hand of cards
def scoreHand(hand, score):
    aces = 0
    score = 0
    
    for card in hand:

        rank = card[:-1]

        if rank in ['J', 'Q', 'K']:
            score += 10

        elif rank == 'A':
            score += 11
            aces += 1

        else:
            score += int(rank)

    while score > 21 and aces > 0:
        score -= 10
        aces -= 1

    return score

#Main function
getUserInput()
deck = Create_Deck()
print("Starting Blackjack simulation, player has $" + str(Starting_Money) + ", player bets $" + str(Bet) + " per round and plays " + str(rounds) + " as long as they don't bankrupt.")
print("")
current_round = 0
while ((Bankruptcy is False) and (rounds > current_round)):
    current_round += 1
    shuffleDeck(current_round)
    roundBet()
    Player_cards = dealCard(Player_cards)
    Dealer_cards = dealCard(Dealer_cards)
    Player_cards = dealCard(Player_cards)
    Dealer_cards = dealCard(Dealer_cards)
    Player_hand = scoreHand(Player_cards, Player_hand)
    Dealer_hand = scoreHand(Dealer_cards, Dealer_hand)
    print("The Player's starting cards are:" + str(Player_cards) + "and their score is" + str(Player_hand))        
    print("")
    print("The Dealer's starting cards are:" + str(Dealer_cards) + "and their score is" + str(Dealer_hand))
    print("")
    if (Player_hand == 21):
        if (Dealer_hand == 21):
            tieRound()
        else:
            winRound()
    elif (Dealer_hand == 21):
        lossRound()
    else:
        while (Player_hand <= 16):
            Player_cards = dealCard(Player_cards)
            Player_hand = scoreHand(Player_cards, Player_hand)
            print("The Player's current cards are:" + str(Player_cards) + "and their score is" + str(Player_hand))
            print("")
            print("The Dealer's current cards are:" + str(Dealer_cards) + "and their score is" + str(Dealer_hand))
            print("")
        if (Player_hand > 21):
            lossRound()
        else:
            while (Dealer_hand < 16):
                Dealer_cards = dealCard(Dealer_cards)
                Dealer_hand = scoreHand(Dealer_cards, Dealer_hand)
                print("The Player's current cards are:" + str(Player_cards) + "and their score is" + str(Player_hand))        
                print("")
                print("The Dealer's current cards are:" + str(Dealer_cards) + "and their score is" + str(Dealer_hand))
                print("")
            if (Dealer_hand > 21):
                winRound()
            else:
                print("The Player's final cards are:" + str(Player_cards) + "and their score is" + str(Player_hand))    
                print("")
                print("The Dealer's final cards are:" + str(Dealer_cards) + "and their score is" + str(Dealer_hand))
                print("")
                compareScore()
    print("The player currently has $" + str(Money))
    print("")
    Bankruptcy = checkBankruptcy()
    Player_cards = []
    Dealer_cards = []
    Player_hand = 0
    Dealer_hand = 0
print("Player has won: " + str(Winnings) + " Player has lost: " + str(Losses) + " Player has ended the game with: " + str(Money) + " after " + str(current_round) + " rounds of Blackjack")
print("")
print("The total change to the player's money is: $" + str(Winnings - Losses))